package com.hmall.search.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.RangeQuery;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.json.JsonData;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.common.utils.BeanUtils;
import com.hmall.search.domian.dto.ItemDTO;
import com.hmall.search.domian.po.Item;
import com.hmall.search.domian.query.ItemPageQuery;
import com.hmall.search.mapper.ItemMapper;
import com.hmall.search.service.IItemService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 商品搜索服务实现类
 * 负责处理商品搜索相关业务逻辑，使用Elasticsearch实现高性能搜索
 */
@Slf4j
@Service
public class ItemServiceImpl extends ServiceImpl<ItemMapper, Item> implements IItemService {

    @Autowired
    private ItemMapper itemMapper;

    @Autowired
    private ElasticsearchClient esClient;

    private static final String INDEX_NAME = "items"; // Elasticsearch索引名称

    /**
     * 根据查询条件搜索商品
     *
     * @param query 搜索查询条件，包含分页、关键字、分类、品牌、价格范围等信息
     * @return 分页包装的商品DTO列表
     */
    @Override
    public Page<ItemDTO> select(ItemPageQuery query) {
        // 健壮性判断
        if (ObjectUtil.isEmpty(query)) {
            throw new RuntimeException("参数非法！");
        }
        try {
            // 1.使用bool查询，构建bool查询参数
            BoolQuery.Builder boolQuery = new BoolQuery.Builder();

            // 2.构建查询的条件
            // 2.1 key-搜索关键字在name、bran、category中查询进 全文检索查询-multi_match
            if (!StrUtil.isBlank(query.getKey())) {
                boolQuery.must(
                        m -> m.multiMatch(
                                mm -> mm.fields("name", "brand", "category").query(query.getKey())
                        )
                );
            }
            // 2.2 category-商品分类 使用term查询精确匹配分类
            if (!StrUtil.isBlank(query.getCategory())) {
                boolQuery.filter(
                        f -> f.term(
                                t -> t.field("category").value(query.getCategory())
                        )
                );
            }
            // 2.3 brand-品牌过滤条件 使用term查询精确匹配品牌
            if (!StrUtil.isBlank(query.getBrand())) {
                boolQuery.filter(
                        f -> f.term(
                                t -> t.field("brand").value(query.getBrand())
                        )
                );
            }
            // 2.4 价格的区间查询
            if (query.getMinPrice() != null && query.getMaxPrice() != null) {
                RangeQuery.Builder rangeQuery = new RangeQuery.Builder().field("price");
                if (query.getMinPrice() != null) {
                    // 大于等于最低价格
                    rangeQuery.gte(JsonData.of(query.getMinPrice()));
                }
                if (query.getMaxPrice() != null) {
                    // 小于等于最高价格
                    rangeQuery.lte(JsonData.of(query.getMaxPrice()));
                }
                boolQuery.filter(f -> f.range(rangeQuery.build()));
            }

            // 3.构建完整的搜索请求
            SearchResponse<ItemDTO> dtoSearchResponse = esClient.search(s -> s
                            .index(INDEX_NAME)
                            .trackTotalHits(t -> t.enabled(true)) // 获取真是记录数
                            .query(q -> q.bool(boolQuery.build())) // 前面构建的查询条件
                            .from((int) (query.getPageNo() - 1) * query.getPageSize()) // 类似于Mysql中的起始索引
                            .size(query.getPageSize()) //每页展示数
                            .sort(sort -> sort.field(f -> f.field("_doc"))) // 使用_doc排序，保持稳定的顺序
                            .highlight(h -> h.fields("name", f -> f.preTags("<em>").postTags("</em>"))) // 商品名称高亮，使用<em>标签
                    , ItemDTO.class);

            // 4.构建返回结果
            // 4.1基础字段的值设置
            List<ItemDTO> objectList = new ArrayList<>();
            for (Hit<ItemDTO> hit : dtoSearchResponse.hits().hits()) {
                // 获取商品数据
                ItemDTO item = hit.source();
                // 处理高亮：将高亮内容替换原始商品名称
                if (hit.highlight() != null && hit.highlight().containsKey("name")) {
                    item.setName(hit.highlight().get("name").get(0).toString());
                }

                // 处理索引中不存在但DTO中定义的字段。设置默认值
                if (item.getSpec() == null) item.setSpec(""); //规格默认空字符串
                if (item.getStatus() == null) item.setStatus(1); //状态默认1-正常
                objectList.add(item);
            }

            // 4.2 构建分页结果
            long total = dtoSearchResponse.hits().total() != null ? dtoSearchResponse.hits().total().value() : 0;
            Page<ItemDTO> page = new Page<>(query.getPageNo(), query.getPageSize(), total);
            page.setRecords(objectList);
            log.info("查询成功！");
            return page;
        } catch (IOException e) {
            log.error("ES查询出错！", e);
            e.printStackTrace();
            log.info("");
            return fallbackToDatabase(query);
        }
    }

    /**
     * 数据库查询回退方法
     * 当Elasticsearch查询失败时，使用数据库进行查询
     */
    private Page<ItemDTO> fallbackToDatabase(ItemPageQuery query) {
        try {
            // 创建MyBatis-Plus分页对象
            Page<Item> page = new Page<>(query.getPageNo(), query.getPageSize());

            // 构建查询条件
            LambdaQueryWrapper<Item> wrapper = new LambdaQueryWrapper<>();

            // 关键字搜索：在商品名称、品牌、分类字段中模糊查询
            if (query.getKey() != null && !query.getKey().isEmpty()) {
                wrapper.like(Item::getName, query.getKey())
                        .or().like(Item::getBrand, query.getKey())
                        .or().like(Item::getCategory, query.getKey());
            }

            // 分类筛选
            if (query.getCategory() != null && !query.getCategory().isEmpty()) {
                wrapper.eq(Item::getCategory, query.getCategory());
            }

            // 品牌筛选
            if (query.getBrand() != null && !query.getBrand().isEmpty()) {
                wrapper.eq(Item::getBrand, query.getBrand());
            }

            // 价格范围筛选
            if (query.getMinPrice() != null) {
                wrapper.ge(Item::getPrice, query.getMinPrice());
            }
            if (query.getMaxPrice() != null) {
                wrapper.le(Item::getPrice, query.getMaxPrice());
            }

            // 执行数据库查询
            Page<Item> result = itemMapper.selectPage(page, wrapper);

            // 将PO对象转换为DTO对象
            Page<ItemDTO> dtoPage = new Page<>(result.getCurrent(), result.getSize(), result.getTotal());
            List<ItemDTO> dtoList = new ArrayList<>();
            for (Item item : result.getRecords()) {
                ItemDTO dto = new ItemDTO();
                BeanUtils.copyProperties(item, dto); // 属性复制
                dtoList.add(dto);
            }
            dtoPage.setRecords(dtoList);

            return dtoPage;
        } catch (Exception e) {
            log.error("数据库查询失败", e);
            // 查询失败时返回空结果，避免影响用户体验
            return new Page<>(query.getPageNo(), query.getPageSize(), 0);
        }
    }
}